package com.jbp.service;
import com.alibaba.fastjson.JSON;
import com.jbp.domin.LuckPool;
import com.jbp.domin.LuckUser;
import com.jbp.model.*;
import com.jbp.redis.RedisConfigService;
import com.jbp.utils.StrToMillsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.FileInputStream;
import java.io.FileWriter;
import java.math.BigDecimal;
import java.util.*;

/**爆率版抽奖工具*/
@Service
public class LuckyUtil {
    @Autowired
    private RedisConfigService redisConfigService;
    @Autowired
    private LotteryService lotteryService;
    @Autowired
    private UserService userService;
    @Autowired
    private RoomService roomService;
    @Autowired
    private PropertyService propertyService;
    @Autowired
    private LuckPoolUserService luckPoolUserService;
    @Autowired
    private LuckPoolRoomService luckPoolRoomService;

    public double getBaseRate(Integer roomId, Integer userId, Integer totalPay) {
        User user = userService.getById(userId);

        double userBaseRate = user.getBaseLuckRate().doubleValue();
        Double roomBaseRate = redisConfigService.getDoubleOrDefault("haoya:luck:room:baserate:" + roomId, 1);
        userBaseRate = Math.min(userBaseRate, roomBaseRate);
        Double ipBaseRate = redisConfigService.getDoubleOrDefault("haoya:luck:ip:baserate:" + user.getIp(), 1);
        userBaseRate = Math.min(userBaseRate, ipBaseRate);
        Double deviceBaseRate = redisConfigService.getDoubleOrDefault("haoya:luck:device:baserate:" + user.getDevice(), 1);
        userBaseRate = Math.min(userBaseRate, deviceBaseRate);

        /*if (totalPay<10000) {
            int ipCount = userService.winCount(user.getIp());
            userBaseRate = Math.max(1 - ipCount * 0.01d, 0.5d);
            userBaseRate = Math.min(userBaseRate, 1.2d);
        }*/
        /*if (userBaseRate<1) {
            System.out.println(user.getName() + "    " + userBaseRate);
        }*/
        return userBaseRate;
    }

    public void setRoomRate(){
        List<RoomRate> list = lotteryService.roomRate();
        for (RoomRate roomRate : list) {
            if (roomRate != null && roomRate.getRoomId() != null) {
                long win = (long) (roomRate.getTincome() * 0.85d - roomRate.getTpay());
                if (win > 100000) {
                    int base = 100000;
                    if (win > 400000) {
                        base = 20000;
                    } else if (win > 350000) {
                        base = 30000;
                    } else if (win > 250000) {
                        base = 50000;
                    }
                    double roomBaseRate = 1 - ((int) win / base) * 0.01d;
                    redisConfigService.set("haoya:luck:room:baserate:" + roomRate.getRoomId(), roomBaseRate, 60 * 60 * 6L);

                    Room room = roomService.getById(roomRate.getRoomId());
                    System.out.println(room.getName() + " pay:" + roomRate.getTpay() + " income:" + roomRate.getTpay() + " win:" + win + " roomBaseRate:" + roomBaseRate);
                }
            }
        }
    }

    public void setIpRate(){
        List<RoomRate> list = lotteryService.ipRate();
        for (RoomRate roomRate : list) {
            if (roomRate != null && roomRate.getIp() != null) {
                long win = (long) (roomRate.getTincome() * 0.85d - roomRate.getTpay());
                if (win > 10000) {
                    int base = 10000;
                    /*if (win>100000) {
                        base = 5000;
                    }*/
                    double roomBaseRate = 1 - ((int) win / base) * 0.01d;
                    redisConfigService.set("haoya:luck:ip:baserate:" + roomRate.getIp(), roomBaseRate, 60 * 60 * 6L);

                    System.out.println(roomRate.getIp() + " pay:" + roomRate.getTpay() + " income:" + roomRate.getTpay() + " win:" + win + " roomBaseRate:" + roomBaseRate);
                }
            }
        }
    }

    public void setDeviceRate(){
        List<RoomRate> list = lotteryService.deviceRate();
        for (RoomRate roomRate : list) {
            if (roomRate != null && roomRate.getDevice() != null) {
                long win = (long) (roomRate.getTincome() * 0.85d - roomRate.getTpay());
                if (win > 30000) {
                    int base = 30000;
                    /*if (win>200000) {
                        base = 30000;
                    } else if (win>100000) {
                        base = 20000;
                    }*/
                    double roomBaseRate = 1 - ((int) win / base) * 0.01d;
                    redisConfigService.set("haoya:luck:device:baserate:" + roomRate.getDevice(), roomBaseRate, 60 * 60 * 6L);

                    System.out.println(roomRate.getDevice() + " pay:" + roomRate.getTpay() + " income:" + roomRate.getTpay() + " win:" + win + " roomBaseRate:" + roomBaseRate);
                }
            }
        }
    }

    public static void printConfig(Map<Integer, Integer> map, int price) {
        //总价值
        long totalCoin = map.entrySet().stream().mapToInt(e -> e.getKey()*e.getValue()).sum();
        //总数量
        long totalSize = map.values().stream().mapToInt(v->v).sum();
        double zbl = BigDecimal.valueOf(totalCoin).divide(BigDecimal.valueOf(totalSize*price), 6, BigDecimal.ROUND_HALF_UP).doubleValue();

        map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(e->{
            double bl = BigDecimal.valueOf(e.getValue() * 100).divide(BigDecimal.valueOf(totalSize), 6, BigDecimal.ROUND_HALF_UP).doubleValue();
            double zb = BigDecimal.valueOf(e.getValue() * e.getKey() * 100).divide(BigDecimal.valueOf(totalCoin), 6, BigDecimal.ROUND_HALF_UP).doubleValue();

            System.out.println(e.getKey()+"钻  "+ e.getValue()+"个   "+" 爆率:"+ bl +"    占比:"+zb);
        });
        System.out.println("总数量："+totalSize+"  总价值:"+totalCoin+"  总爆率："+zbl);
    }

    public static void tjUser(List<UserLuck> userLucks) {
        //总人数
        int totalCount = userLucks.size();
        //盈利人数
        double winRate = 0.85d;
        int winCount = (int) userLucks.stream().filter(user-> user.totalIncome* winRate-user.totalPay >0).count();
        //总盈利
        long sumWin = userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay > 0).mapToLong(user -> (int) (user.totalIncome* winRate-user.totalPay)).sum();
        // 平均盈利
        long avgWin = sumWin/winCount;
        //最大盈利
        int maxWin = userLucks.stream().filter(user -> 1d*user.totalIncome / user.totalPay > winRate && user.totalIncome* winRate-user.totalPay > 0).mapToInt(user -> (int) (user.totalIncome* winRate-user.totalPay)).max().getAsInt();
        //最大爆率
        double maxRate = userLucks.stream().mapToDouble(user -> 1d*user.totalIncome*1d / user.totalPay).max().getAsDouble();

        //亏损人数
        int loseCount = totalCount - winCount;
        //总亏损
        long sumLose = userLucks.stream().filter(user -> (1d*user.totalIncome / user.totalPay <= winRate) || (user.totalIncome* winRate-user.totalPay < 0)).mapToLong(user -> (int) (user.totalIncome* winRate-user.totalPay)).sum();
        //平均亏损
        long avgLose = loseCount==0 ? loseCount : sumLose/loseCount;
        //最大亏损
        int maxLose = userLucks.stream().filter(user -> (1d*user.totalIncome / user.totalPay <= winRate) || (user.totalIncome* winRate-user.totalPay < 0)).mapToInt(user -> (int) (user.totalIncome* winRate-user.totalPay)).min().getAsInt();
        double minRate = userLucks.stream().mapToDouble(user -> 1d*user.totalIncome*1d / user.totalPay).min().getAsDouble();

        System.out.println("抽奖总人数："+totalCount);
        System.out.println("盈利人数:"+winCount+"  平均盈利:"+avgWin+" 最大盈利："+maxWin + " 最大爆率:"+maxRate);
        System.out.println("亏损人数:"+loseCount+"  平均亏损:"+avgLose+" 最大亏损："+maxLose + " 最小爆率:"+minRate);

        System.out.println("---用户亏损统计----");
        System.out.println("亏损大于10w钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay < -100000).count());
        System.out.println("亏损5w-10w钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= -100000 && user.totalIncome* winRate-user.totalPay < -50000).count());
        System.out.println("亏损<5w钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= -50000&& user.totalIncome* winRate-user.totalPay < 0).count());
        System.out.println("---用户盈利统计----");
        System.out.println("200<盈利<1000钻: "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 200 && user.totalIncome* winRate-user.totalPay < 1000).count());
        System.out.println("1000<盈利<2000钻: "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 1000 && user.totalIncome* winRate-user.totalPay < 2000).count());
        System.out.println("2000<盈利<5000钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 2000 && user.totalIncome* winRate-user.totalPay < 5000).count());
        System.out.println("5000<盈利<10000钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 5000 && user.totalIncome* winRate-user.totalPay < 10000).count());
        System.out.println("10000<盈利<100000钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 10000 && user.totalIncome* winRate-user.totalPay < 100000).count());

        System.out.println("---用户爆率统计----");
        System.out.println("爆率<0.7： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 0.7d).count());
        System.out.println("0.7<爆率<0.8： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 0.8d && user.totalIncome*1d / user.totalPay >= 0.7d).count());
        System.out.println("0.8<爆率<0.9： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 0.9d && user.totalIncome*1d / user.totalPay >= 0.8d).count());
        System.out.println("0.9<爆率<1： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1d && user.totalIncome*1d / user.totalPay >= 0.9d).count());
        System.out.println("1<爆率<1.1："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.1d && user.totalIncome*1d / user.totalPay >= 1d).count());
        System.out.println("1.1<爆率<1.15："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.15d && user.totalIncome*1d / user.totalPay >= 1.1d).count());
        System.out.println("1.15<爆率<1.2："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.2d && user.totalIncome*1d / user.totalPay >= 1.15d).count());
        System.out.println("1.2<爆率<1.3："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.3d && user.totalIncome*1d / user.totalPay >= 1.2d).count());
        System.out.println("1.3<爆率<1.5："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.5d && user.totalIncome*1d / user.totalPay >= 1.3d).count());
        System.out.println("1.5<爆率<1.8："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.8d && user.totalIncome*1d / user.totalPay >= 1.5d).count());
        System.out.println("1.8<爆率<2.5："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 2.5d && user.totalIncome*1d / user.totalPay >= 1.8d).count());
        System.out.println("2.5<爆率<5："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 5d && user.totalIncome*1d / user.totalPay >= 2.5d).count());
        System.out.println("5<爆率："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay >= 5d).count());
    }

    /**根据预期概率取C值
     * */
    static int getC(double rate){
        LinkedHashMap<Integer, Double> cConfig = getCConfig();
        for (Map.Entry<Integer, Double> e : cConfig.entrySet()) {
            Object o = e.getValue();
            if (o instanceof BigDecimal) {
                BigDecimal b =  (BigDecimal) o;
                if (b.doubleValue()>rate) {
                    return e.getKey();
                }
            }

        }
        return 0;
    }

    /**
     * 获取C值表
     * key c值, value 预期概率
     * */
    static LinkedHashMap<Integer, Double> getCConfig(){
        try {
            LinkedHashMap<Integer, Double> map = JSON.parseObject(new FileInputStream("c.json"), LinkedHashMap.class);
            if (map==null || map.isEmpty()) {
                map = new LinkedHashMap<>();
                for (int prob = 1; prob <= 10000; prob++) {
                    double crit = testCrit(prob, 10000);
                    System.out.println("概率：" + 100.d / crit + " C:" + prob);
                    map.put(prob, 100.d / crit);
                }
                String json = JSON.toJSONString(map);

                FileWriter fileWriter = new FileWriter("c.json");
                fileWriter.write(json);
                fileWriter.flush();
                fileWriter.close();
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //测试暴击 prob 初始爆率 len 模拟次数
    static double testCrit(int prob, int len) {
        List<Integer> list = new ArrayList<>();
        for (int i=0 ; i<len; i++){
            int times = 0;
            boolean crit = false;
            while (!crit) {
                times++;
                crit = baoji(prob * times);
            }
            list.add(times);
//            System.out.println(times);
        }
//        System.out.println("平均："+list.stream().mapToInt(Integer::intValue).average());
//        System.out.println("最大："+list.stream().mapToInt(Integer::intValue).max());
//        System.out.println("最小："+list.stream().mapToInt(Integer::intValue).min());
        return list.stream().mapToInt(Integer::intValue).average().getAsDouble();
    }

    /**
     * 是否暴击
     * prob 概率 一万次中奖次数
     * */
    static boolean baoji(int prob) {
        int r = new Random().nextInt(10000);
        return r<prob;
    }

    /**
     * 抽奖
     * */
    static int nodeLuck(Node node,double poolRate, int min, int max, Map<String, Integer> nMap, Map<String, Integer> allMap, double baseRate) {
        if (node.luck!=0) {
            return node.luck;
        }
        Integer times = nMap.getOrDefault(node.name, 1);
        Integer timesAll = allMap.getOrDefault(node.name, 1);

        int rate = node.getRate(min, max);
        int realRate = 10000;
        if (rate != 10000) {
            realRate = (int) (rate * (Math.max(timesAll, times * 0.7d)) * poolRate * baseRate);
        }
        if (baoji(realRate)) {
            int right = nodeLuck(node.right, poolRate, min, max, nMap, allMap, baseRate);
            if (right>0) {
                allMap.put(node.name, 1);
                nMap.put(node.name, 1);
            }
            return right;
        } else {
            int left = nodeLuck(node.left,poolRate, min, max, nMap, allMap, baseRate);
            if (left>0) {
                nMap.put(node.name, times+1);
                allMap.put(node.name, timesAll+1);
            }
            return left;
        }
    }


    /**
     * 计算奖池配置
     * */
    static int calPercent(Node node, Map<Integer, Integer> map) {
        if (node==null || node.luck!=0) {
            return map.getOrDefault(node.luck, 0);
        } else {
            int right = calPercent(node.right, map);
            int left = calPercent(node.left, map);
            double percent = right+left==0 ? 0 : BigDecimal.valueOf(100d*right/(left+right)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            System.out.println(node.name+" p:"+percent + "  r:"+node.rate + " c:"+getC(percent));
            return right+left;
        }
    }

    static double getPoolRate(int pool, int giftSize, double winRate, int price) {
        int out = (int) (pool - giftSize*winRate*price);
        double rate = 0.9 + 0.1 * (out / 10000);
        rate = Math.max(0.5, rate);
        rate = Math.min(1.7, rate);
        return rate;
    }

    /**补礼物， 更新概率树*/
    static boolean patch(Node node, int luck, Map<String, Integer> nMap, Map<String, Integer> allMap) {
        if (node==null) {
            return false;
        }
        if (node.luck!=0) {
            return node.luck==luck;
        }

        if (patch(node.right, luck, nMap, allMap)) {
//            allMap.put(node.name, 1);
            nMap.put(node.name, 1);
            return true;
        }
        return patch(node.left, luck, nMap, allMap);
    }

    public static int luckRandom(Map<Integer, Integer> config, double min, double biggest) {
        LinkedHashMap<Integer, Integer> pool = new LinkedHashMap<>();
        config.keySet().stream().filter(i -> i<=biggest && i>=min).forEach(k->{
            pool.put(k, config.get(k));
        });
        int sum = pool.values().stream().mapToInt(Integer::intValue).sum();
        int i = new Random().nextInt(sum);
        int t = 0;
        for (Map.Entry<Integer, Integer> entry : pool.entrySet()) {
            t += entry.getValue();
            if (i<t) {
                return entry.getKey();
            }
        }
        throw new RuntimeException("抽奖错误");
    }

    public static void geneTree() {
        int[] gifts = {8, 13, 18, 28, 58, 88, 188, 518, 888, 2188, 3388, 5188, 9999, 13888, 26888, 33888, 55888, 68888};
        System.out.println(gifts.length);
    }
    public static void geneTree(int[] gifts, Node root) {
        System.out.println(gifts.length);
    }

    /**获取用户数据 当天*/
    public LuckUser getLuckUser(Integer userId, int price){
        String date = StrToMillsUtil.millsToStrDate(new Date(), null);
        String key = "kchat:luck:user:"+price+":"+userId +":"+date;
        LuckUser luckUser = (LuckUser) redisConfigService.getJson(key, LuckUser.class);
        if (luckUser==null) {
            luckUser = new LuckUser();
        }
        luckUser.setUserId(userId);
        luckUser.setPrice(price);
        return luckUser;
    }

    /**跟新用户数据*/
    public boolean updateLuckUser(LuckUser luckUser) {
        String date = StrToMillsUtil.millsToStrDate(new Date(), null);
        String key = "kchat:luck:user:"+luckUser.getPrice()+":"+luckUser.getUserId()+":"+date;
        return redisConfigService.setJson(key, luckUser);
    }

    /**获取奖池数据*/
    public LuckPool getLuckPool(int price, String type, Integer roomId, Integer userId){
        double winRate = Double.parseDouble(propertyService.getV("winRateASmall"));
        LuckPoolRoom luckPoolRoom = luckPoolRoomService.getByRoomId(roomId);
        LuckPoolUser luckPoolUser = luckPoolUserService.getByUserId(userId);
        String poolPrex = "";
        if (luckPoolUser!=null) {
            poolPrex = "u:"+userId+":";
            winRate *= luckPoolUser.getRate().doubleValue() / 1.17d;
        } else if (luckPoolRoom!=null) {
            poolPrex = "r:"+roomId+":";
            winRate *= luckPoolRoom.getRate().doubleValue() / 1.17d;
        }

        String poolName = type+":"+poolPrex;

        String key = "kchat:luck:pool:"+price+":"+poolName;
        LuckPool luckPool = (LuckPool) redisConfigService.getJson(key, LuckPool.class);
        if (luckPool==null) {
            luckPool = new LuckPool();
        }
        if (luckPool.getPoolMap()==null) {
            luckPool.setPoolMap(new LinkedHashMap<>());
        }
        luckPool.setWinRate(winRate);
        luckPool.setName(poolName);
        luckPool.setPrice(price);
        return luckPool;
    }

    /**跟新用户数据*/
    public boolean updateLuckPool(LuckPool luckPool) {
        String key = "kchat:luck:pool:"+luckPool.getPrice()+":"+luckPool.getName();
        return redisConfigService.setJson(key, luckPool);
    }

    /**补池增长*/
    public void addPatch(int coin) {
        String key = "haoya:luck:patchpool";
        int patchPool =  redisConfigService.getInt(key);
        redisConfigService.set(key, patchPool + coin);
    }
}


